#include "slave.h"

#include "../base/mq.h"
#include "../base/dl.h"

#include "slave_protocol.h"
#include "master.h"
#include "msg_api.h"

#include <stdio.h>
#include <malloc.h>
#include <assert.h>

C_CODE_BEGIN


struct slave {
	/* this message queue is a pointer reference to the message queue managed by the master 
	 * The slave just have a weak pointer of this message queue .
	 */
	struct msgqueue* msgqueue;
	/* number of message has been dispatched */
	size_t nom; 
	/* handler of dynamic library */
	dl_handler dl;
	/* slave protocol provided by the input dll */
	struct slave_protocol slp;
	/* slave owned api, this api pointer will store
	 * hidden information about the slave itself. 
	 * Simply puts it, this api pointer will also has
	 * 2 hidden memory pointer, first points to the master
	 * second point to the slave itself .
	 */
	struct slave_api* api;
	/*
	 * Actually, I really do not want to add more flags for slave,but compared to implementing a
	 * concurrent access map, adding a flag may be much better. This status flag is used to store
	 * the status information for the slave.
	 */
	int status;
};


#define SLAVE_STRUCTURE_SIZE \
	sizeof(struct slave) + sizeof(struct slave_api) + sizeof(void*)*2 


static struct slave_api* _api_set_master( struct slave_api* api , struct master* m ) {
	*(struct master**)((char*)(api) + sizeof(struct slave_api)) = m;
	return api;
}

static struct slave_api* _api_set_slave( struct slave_api* api , struct slave* s ) {
	*(struct slave**)((char*)(api) + sizeof(struct slave_api) + sizeof(void*)) = s;
	return api;
}

struct master* _slave_api_ref_master( struct slave_api* sapi ) {
	return *(struct master**)( (char*)(sapi) + sizeof(struct slave_api) );
}

struct slave* _slave_api_ref_slave( struct slave_api* sapi ) {
	struct slave* sl = *(struct slave**)((char*)(sapi) + sizeof(struct slave_api) + sizeof(void*));
	return sl;
}


typedef int (*entry)( struct slave_protocol* );

#define SLAVE_PROTO_CLEAN(p) \
	(p).argument = NULL; \
	(p).cb = NULL; \
	(p).init=NULL; \
	(p).clean=NULL; \
	(p).notify=NULL; \
	(p).slavename=NULL


static void _slave_unload_only( struct slave* sl ) {
	if( sl->dl != DL_INVALID_HANDLE )
		dl_close(sl->dl);
}

void _slave_destroy( struct slave* sl ) {
	msgqueue_destroy(sl->msgqueue);
	free(sl);
}

void _slave_unload( struct slave* s ) {
	/* close the dl handler */
	if( s->dl != DL_INVALID_HANDLE )
		dl_close(s->dl);
	_slave_destroy(s);
}

static struct slave* _slave_load_only( 
	struct slave* sl,
	const char* dlpath , 
	struct msgqueue* mq , 
	void (*deleter)(struct slave* sl),
	struct master* m ) {
	int ret;
	entry e;
	/* FIX : this message queue creation is originally put inside of the command , this is very meaningful
	 * to put this creation code there. It is wired indeed , but I think I've forgotten the reason . NVM, 
	 * I will put it back to where it belongs */
	sl->msgqueue = mq;
	sl->nom = SLAVE_RESERVE_SID_VALUE;
	sl->api = (struct slave_api*)((char*)(sl) + sizeof(struct slave));
	sl->api->sendmessage = _msgapi_send_msg;
	/* set the hidden pointer */
	_api_set_master(sl->api,m);
	_api_set_slave(sl->api,sl);
	sl->dl = dl_open(dlpath);
	if( sl->dl == DL_INVALID_HANDLE ) {
		/* error for dynamic library loading */
		master_write_log(m,"warn","Can not load slave library: %s\n",dlpath);
		deleter(sl);
		return NULL;
	}
	e = (entry)dl_find_symbol(sl->dl,"entry");
	if(e==NULL) {
		master_write_log(m,"warn","Can not load slave entry function: %s\n",dlpath);
		deleter(sl);
		return NULL;
	}
	/*  make all the sl->slp area to be NULL  */
	SLAVE_PROTO_CLEAN(sl->slp);
	/* invoke the entry function */
	ret = e(&(sl->slp));
	if( ret<0 ) {
		master_write_log(m,"warn","The slave %s's entry function cannot be invoked!\n" \
								  "Check out more information from slave %s.\n",sl->slp.slavename,sl->slp.slavename);
		deleter(sl);
		return NULL;
	}
	/* check the must have area of slp */
	if( sl->slp.slavename == NULL ||
		sl->slp.cb == NULL ) {
			master_write_log(m,"warn","The slave protocol must at least have a" \
				"callback function and slavename!\n",sl->slp.slavename,sl->slp.slavename);
			deleter(sl);
			return NULL;	
	}
	return sl;
}

struct slave* _slave_load( const char* dlpath , struct master* m ) {
	return _slave_load_only(
		malloc( SLAVE_STRUCTURE_SIZE ),
		dlpath,
		msgqueue_create( SLAVE_MAX_CACHE_MESSAGE_LEN , sizeof(struct message) ),
		_slave_unload,
		m);
}

const char* _slave_name( struct slave* sl ) {
	return sl->slp.slavename;
}

size_t _slave_new_sid( struct slave* sl ) {
#ifdef _MSC_VER
	return InterlockedIncrement( (volatile LONG*)&(sl->nom) );
#elif __GNUC__
	return __sync_add_and_fetch( &(sl->nom) , 1 );
#else
	return ++(sl->nom);
#endif
}

void _slave_cb_invoke( struct slave* sl ,struct message* msg  ) {
	assert(sl->slp.cb!=NULL);
	sl->slp.cb( msg, sl->slp.argument , _slave_get_api(sl));
}

int _slave_init_invoke( struct slave* sl ) {
	if( sl->slp.init )
		return sl->slp.init(sl->slp.argument);
	/* no init function we treat it as successfully */
	return 1;
}

void _slave_notification_invoke( struct slave* sl ) {
	if( (sl->slp.notify) != NULL )
		sl->slp.notify(_slave_get_api(sl),sl->slp.argument);
}

void _slave_clean_invoke( struct slave* sl ) {
	if( sl->slp.clean )
		sl->slp.clean(sl->slp.argument);
}

struct msgqueue* _slave_ref_msgqueue( struct slave* sl) {
	return sl->msgqueue;
}

struct slave_api* _slave_get_api( struct slave* sl ) {
	return sl->api;
}

struct slave* _slave_compose( struct slave_protocol* pro , struct slave_api* sapi, struct master* m ) {
	struct slave * sl = malloc( SLAVE_STRUCTURE_SIZE ) ;
	sl->slp = *pro;
	sl->msgqueue = msgqueue_create(SLAVE_MAX_CACHE_MESSAGE_LEN,sizeof(struct message));
	sl->nom = 0;
	sl->api = (struct slave_api*)((char*)(sl) + sizeof(struct slave));
	sl->api->sendmessage = sapi->sendmessage;
	_api_set_slave(sl->api,sl);
	_api_set_master(sl->api,m);
	sl->dl = DL_INVALID_HANDLE;
	assert( pro->cb && pro->slavename );
	return sl;
}


bool _slave_replace_load( struct slave* sl , const char* dlpath ) {
	char buffer[ SLAVE_STRUCTURE_SIZE ];
	struct slave* tp_sl = buffer;
	struct slave* ret_sl = 
		_slave_load_only(
		tp_sl,
		dlpath,
		sl->msgqueue,
		_slave_unload_only,
		_slave_api_ref_master(_slave_get_api(sl)));
	if( ret_sl == NULL ) {
		master_write_log(_slave_api_ref_master(_slave_get_api(sl)),
			"warn","Cannot replace the slave with dlpath: %s",dlpath);
		return false;
	}
	/* Now we have successfully load the target slave , we just need to do
	 * the swap operation here. 
	/* 1. clean the original slave  */
	memcpy( sl , buffer ,SLAVE_STRUCTURE_SIZE );
}
	


C_CODE_END
